feat: recognize generated schema types (SchemaTypeName + UnwrapSchema)#20
Conversation
Add the Schema interface (SchemaTypeName), the UnwrapSchema reflection helper, and the DgraphMapper interface (record.go). The client unwraps schema-defining values at the mutation and query boundary so generated wrapper types route to their backing schema struct. Plain structs do not implement Schema and are unaffected — UnwrapSchema is identity for them.
There was a problem hiding this comment.
3 issues found across 3 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
…Schema Addresses review feedback on generated-schema-type routing: - UnwrapSchema no longer panics on a typed nil pointer: it returns the value untouched instead of invoking Unwrap on a nil receiver that may dereference. - UnwrapSchema now finds a pointer-receiver Unwrap on a wrapper passed by value, by looking the method up on an addressable copy (a value's method set excludes pointer-receiver methods). Tests: - Add unit tests for the typed-nil-pointer and value-wrapper cases. - Add an integration test (TestClientUnwrapsWrapperThroughRealMutation) that inserts a wrapper through the real client and reads it back, so a mutation method dropping its UnwrapSchema call is caught — the prior tests exercised UnwrapSchema in isolation through a local mock and would not catch that. Docs: add a runnable ExampleSchema showing the wrapper/Unwrap pattern.
Insert, InsertRaw, and Upsert accept a slice of objects, but UnwrapSchema only unwrapped the top-level value. A []*Wrapper passed each wrapper through un-unwrapped, so dgman reflected over it and failed with "cannot set uid/", persisting nothing. UnwrapSchema now maps over slices and arrays: it unwraps each element into a typed []T (or []any when inner types differ) and returns the original slice untouched when no element is a wrapper, so dgman's UID write-back still lands on existing callers' backing arrays. Add the SPDX copyright header to record.go and record_test.go to match the rest of the repo. Addresses review feedback from @matthewmcneely on PR #20.
|
@matthewmcneely Both threads addressed in 7c5fccd: |
matthewmcneely
left a comment
There was a problem hiding this comment.
Both change requests resolved on 7c5fccd:
- Batch unwrapping (client.go):
UnwrapSchemanow has areflect.Slice/reflect.Arraybranch that unwraps element-wise. Verified it returns the original slice untouched when no element is a wrapper (preserving dgman's UID write-back through the backing array), builds a typed[]Tfor the homogeneous batch case, and falls back to[]anyfor mixed inner types. So[]*Wrapperno longer fails with the opaquecannot set uid/. - SPDX headers added to record.go and record_test.go.
Covered by TestUnwrapSchema_{SliceOfWrappersUnwrapsEach,SliceOfPlainStructsUnchanged,EmptySlicePassthrough,MixedInnerTypesFallBackToAny,ArrayOfWrappersUnwrapsEach} plus the real-client slice mutation test.
What this adds
A small mechanism that lets the client recognize generated schema/wrapper
types and route them to their backing schema struct. It is the integration
point that lets a code generator (e.g.
modusgraph-gen) emit wrapper types theclient understands, without the client depending on the generator.
Schemainterface —SchemaTypeName() string; generated schema structsimplement it.
UnwrapSchema(obj)— returns the schema record insideobj: identity for adirect
Schema, the result ofUnwrap()when that result is aSchema, andidentity for everything else.
UnwrapSchema,so a wrapper routes to its inner record while plain structs pass through
untouched.
Self-contained and additive: builds and tests green.
The problem it solves
Code generators want to hand the client a richer wrapper type (a fluent builder,
a domain object) rather than the bare schema struct. The client only knows how
to reflect over schema structs.
UnwrapSchemais the bridge: a wrapper exposesUnwrap()returning its backingSchema, and the client unwraps it before theexisting reflection pipeline runs. Plain structs implement neither
SchemanorUnwrap, so they are unaffected —UnwrapSchemais identity for them.Review round — changes since first push
Addresses the automated review (cubic):
UnwrapSchemainvokedUnwrap()on atyped nil pointer, which panics if the method dereferences the receiver. It
now returns a nil pointer untouched.
whose
Unwrap()has a pointer receiver was not unwrapped, because a value'smethod set excludes pointer-receiver methods.
UnwrapSchemanow looks themethod up on an addressable copy.
UnwrapSchemathrough a localrecordingClient, so a real mutation methoddropping its
UnwrapSchemacall would go undetected. Added an integrationtest that inserts a wrapper through the real client and reads it back.
Tests
TestClientUnwrapsWrapperThroughRealMutation— inserts a wrapper via the realclient, asserts the inner record persisted and the UID was written back.
Documentation
A runnable
ExampleSchemashowing the wrapper/Unwrappattern, alongside theexisting doc comments on
SchemaandUnwrapSchema.